1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.base.Function;
21  import com.google.common.collect.Table.Cell;
22  import com.google.common.collect.testing.MapInterfaceTest;
23  import com.google.common.collect.testing.SampleElements;
24  import com.google.common.collect.testing.TestSetGenerator;
25  import com.google.common.collect.testing.features.CollectionFeature;
26  import com.google.common.collect.testing.features.CollectionSize;
27  import com.google.common.collect.testing.features.Feature;
28  
29  import junit.framework.TestCase;
30  
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.SortedMap;
35  
36  /**
37   * Collection tests for {@link Table} implementations.
38   *
39   * @author Jared Levy
40   * @author Louis Wasserman
41   */
42  @GwtCompatible(emulated = true)
43  public class TableCollectionTest extends TestCase {
44  
45    private static final Feature<?>[] COLLECTION_FEATURES = {
46      CollectionSize.ANY,
47      CollectionFeature.ALLOWS_NULL_QUERIES
48    };
49  
50    private static final Feature<?>[] COLLECTION_FEATURES_ORDER = {
51      CollectionSize.ANY,
52      CollectionFeature.KNOWN_ORDER,
53      CollectionFeature.ALLOWS_NULL_QUERIES
54    };
55  
56    private static final Feature<?>[] COLLECTION_FEATURES_REMOVE = {
57      CollectionSize.ANY,
58      CollectionFeature.SUPPORTS_REMOVE,
59      CollectionFeature.ALLOWS_NULL_QUERIES
60    };
61  
62    private static final Feature<?>[] COLLECTION_FEATURES_REMOVE_ORDER = {
63      CollectionSize.ANY,
64      CollectionFeature.KNOWN_ORDER,
65      CollectionFeature.SUPPORTS_REMOVE,
66      CollectionFeature.ALLOWS_NULL_QUERIES
67    };
68  
69    private static void populateForRowKeySet(
70        Table<String, Integer, Character> table, String[] elements) {
71      for (String row : elements) {
72        table.put(row, 1, 'a');
73        table.put(row, 2, 'b');
74      }
75    }
76  
77    private static void populateForColumnKeySet(
78        Table<Integer, String, Character> table, String[] elements) {
79      for (String column : elements) {
80        table.put(1, column, 'a');
81        table.put(2, column, 'b');
82      }
83    }
84  
85    private static void populateForValues(
86        Table<Integer, Character, String> table, String[] elements) {
87      for (int i = 0; i < elements.length; i++) {
88        table.put(i, 'a', elements[i]);
89      }
90    }
91  
92    private static abstract class TestCellSetGenerator
93        implements TestSetGenerator<Cell<String, Integer, Character>> {
94      @Override
95      public SampleElements<Cell<String, Integer, Character>> samples() {
96        return new SampleElements<Cell<String, Integer, Character>>(
97            Tables.immutableCell("bar", 1, 'a'),
98            Tables.immutableCell("bar", 2, 'b'),
99            Tables.immutableCell("foo", 3, 'c'),
100           Tables.immutableCell("bar", 1, 'b'),
101           Tables.immutableCell("cat", 2, 'b'));
102     }
103 
104     @Override
105     public Set<Cell<String, Integer, Character>> create(
106         Object... elements) {
107       Table<String, Integer, Character> table = createTable();
108       for (Object element : elements) {
109         @SuppressWarnings("unchecked")
110         Cell<String, Integer, Character> cell
111             = (Cell<String, Integer, Character>) element;
112         table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
113       }
114       return table.cellSet();
115     }
116 
117     abstract Table<String, Integer, Character> createTable();
118 
119     @Override
120     @SuppressWarnings("unchecked")
121     public Cell<String, Integer, Character>[] createArray(int length) {
122       return (Cell<String, Integer, Character>[]) new Cell<?, ?, ?>[length];
123     }
124 
125     @Override
126     public List<Cell<String, Integer, Character>> order(
127         List<Cell<String, Integer, Character>> insertionOrder) {
128       return insertionOrder;
129     }
130   }
131 
132   private static abstract class MapTests
133       extends MapInterfaceTest<String, Integer> {
134 
135     MapTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
136         boolean supportsClear, boolean supportsIteratorRemove) {
137       super(false, allowsNullValues, supportsPut, supportsRemove, supportsClear,
138           supportsIteratorRemove);
139     }
140 
141     @Override protected String getKeyNotInPopulatedMap() {
142       return "four";
143     }
144 
145     @Override protected Integer getValueNotInPopulatedMap() {
146       return 4;
147     }
148   }
149 
150   private static abstract class RowTests extends MapTests {
151     RowTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
152         boolean supportsClear, boolean supportsIteratorRemove) {
153       super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
154           supportsIteratorRemove);
155     }
156 
157     abstract Table<Character, String, Integer> makeTable();
158 
159     @Override protected Map<String, Integer> makeEmptyMap() {
160       return makeTable().row('a');
161     }
162 
163     @Override protected Map<String, Integer> makePopulatedMap() {
164       Table<Character, String, Integer> table = makeTable();
165       table.put('a', "one", 1);
166       table.put('a', "two", 2);
167       table.put('a', "three", 3);
168       table.put('b', "four", 4);
169       return table.row('a');
170     }
171   }
172 
173   public static class HashRowTests extends RowTests {
174     public HashRowTests() {
175       super(false, true, true, true, true);
176     }
177 
178     @Override Table<Character, String, Integer> makeTable() {
179       return HashBasedTable.create();
180     }
181   }
182 
183   public static class TreeRowTests extends RowTests {
184     public TreeRowTests() {
185       super(false, true, true, true, true);
186     }
187 
188     @Override Table<Character, String, Integer> makeTable() {
189       return TreeBasedTable.create();
190     }
191   }
192 
193   public static class TransposeRowTests extends RowTests {
194     public TransposeRowTests() {
195       super(false, true, true, true, false);
196     }
197 
198     @Override Table<Character, String, Integer> makeTable() {
199       Table<String, Character, Integer> original = TreeBasedTable.create();
200       return Tables.transpose(original);
201     }
202   }
203 
204   private static final Function<Integer, Integer> DIVIDE_BY_2
205       = new Function<Integer, Integer>() {
206         @Override public Integer apply(Integer input) {
207           return (input == null) ? null : input / 2;
208         }
209   };
210 
211   public static class TransformValueRowTests extends RowTests {
212     public TransformValueRowTests() {
213       super(false, false, true, true, true);
214     }
215 
216     @Override Table<Character, String, Integer> makeTable() {
217       Table<Character, String, Integer> table = HashBasedTable.create();
218       return Tables.transformValues(table, DIVIDE_BY_2);
219     }
220 
221     @Override protected Map<String, Integer> makePopulatedMap() {
222       Table<Character, String, Integer> table = HashBasedTable.create();
223       table.put('a', "one", 2);
224       table.put('a', "two", 4);
225       table.put('a', "three", 6);
226       table.put('b', "four", 8);
227       return Tables.transformValues(table, DIVIDE_BY_2).row('a');
228     }
229   }
230 
231   public static class UnmodifiableHashRowTests extends RowTests {
232     public UnmodifiableHashRowTests() {
233       super(false, false, false, false, false);
234     }
235 
236     @Override Table<Character, String, Integer> makeTable() {
237       Table<Character, String, Integer> table = HashBasedTable.create();
238       return Tables.unmodifiableTable(table);
239     }
240 
241     @Override protected Map<String, Integer> makePopulatedMap() {
242       Table<Character, String, Integer> table = HashBasedTable.create();
243       table.put('a', "one", 1);
244       table.put('a', "two", 2);
245       table.put('a', "three", 3);
246       table.put('b', "four", 4);
247       return Tables.unmodifiableTable(table).row('a');
248     }
249   }
250 
251   public static class UnmodifiableTreeRowTests extends RowTests {
252     public UnmodifiableTreeRowTests() {
253       super(false, false, false, false, false);
254     }
255 
256     @Override Table<Character, String, Integer> makeTable() {
257       RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
258       return Tables.unmodifiableRowSortedTable(table);
259     }
260 
261     @Override protected Map<String, Integer> makePopulatedMap() {
262       RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
263       table.put('a', "one", 1);
264       table.put('a', "two", 2);
265       table.put('a', "three", 3);
266       table.put('b', "four", 4);
267       return Tables.unmodifiableRowSortedTable(table).row('a');
268     }
269   }
270 
271   private static abstract class ColumnTests extends MapTests {
272     ColumnTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
273         boolean supportsClear, boolean supportsIteratorRemove) {
274       super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
275           supportsIteratorRemove);
276     }
277 
278     abstract Table<String, Character, Integer> makeTable();
279 
280     @Override protected Map<String, Integer> makeEmptyMap() {
281       return makeTable().column('a');
282     }
283 
284     @Override protected Map<String, Integer> makePopulatedMap() {
285       Table<String, Character, Integer> table = makeTable();
286       table.put("one", 'a', 1);
287       table.put("two", 'a', 2);
288       table.put("three", 'a', 3);
289       table.put("four", 'b', 4);
290       return table.column('a');
291     }
292   }
293 
294   public static class HashColumnTests extends ColumnTests {
295     public HashColumnTests() {
296       super(false, true, true, true, false);
297     }
298 
299     @Override Table<String, Character, Integer> makeTable() {
300       return HashBasedTable.create();
301     }
302   }
303 
304   public static class TreeColumnTests extends ColumnTests {
305     public TreeColumnTests() {
306       super(false, true, true, true, false);
307     }
308 
309     @Override Table<String, Character, Integer> makeTable() {
310       return TreeBasedTable.create();
311     }
312   }
313 
314   public static class TransposeColumnTests extends ColumnTests {
315     public TransposeColumnTests() {
316       super(false, true, true, true, true);
317     }
318 
319     @Override Table<String, Character, Integer> makeTable() {
320       Table<Character, String, Integer> original = TreeBasedTable.create();
321       return Tables.transpose(original);
322     }
323   }
324 
325   public static class TransformValueColumnTests extends ColumnTests {
326     public TransformValueColumnTests() {
327       super(false, false, true, true, false);
328     }
329 
330     @Override Table<String, Character, Integer> makeTable() {
331       Table<String, Character, Integer> table = HashBasedTable.create();
332       return Tables.transformValues(table, DIVIDE_BY_2);
333     }
334 
335     @Override protected Map<String, Integer> makePopulatedMap() {
336       Table<String, Character, Integer> table = HashBasedTable.create();
337       table.put("one", 'a', 1);
338       table.put("two", 'a', 2);
339       table.put("three", 'a', 3);
340       table.put("four", 'b', 4);
341       return Tables.transformValues(table, DIVIDE_BY_2).column('a');
342     }
343   }
344 
345   public static class UnmodifiableHashColumnTests extends ColumnTests {
346     public UnmodifiableHashColumnTests() {
347       super(false, false, false, false, false);
348     }
349 
350     @Override Table<String, Character, Integer> makeTable() {
351       Table<String, Character, Integer> table = HashBasedTable.create();
352       return Tables.unmodifiableTable(table);
353     }
354 
355     @Override protected Map<String, Integer> makePopulatedMap() {
356       Table<String, Character, Integer> table = HashBasedTable.create();
357       table.put("one", 'a', 1);
358       table.put("two", 'a', 2);
359       table.put("three", 'a', 3);
360       table.put("four", 'b', 4);
361       return Tables.unmodifiableTable(table).column('a');
362     }
363   }
364 
365   public static class UnmodifiableTreeColumnTests extends ColumnTests {
366     public UnmodifiableTreeColumnTests() {
367       super(false, false, false, false, false);
368     }
369 
370     @Override Table<String, Character, Integer> makeTable() {
371       RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
372       return Tables.unmodifiableRowSortedTable(table);
373     }
374 
375     @Override protected Map<String, Integer> makePopulatedMap() {
376       RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
377       table.put("one", 'a', 1);
378       table.put("two", 'a', 2);
379       table.put("three", 'a', 3);
380       table.put("four", 'b', 4);
381       return Tables.unmodifiableRowSortedTable(table).column('a');
382     }
383   }
384 
385   private static abstract class MapMapTests
386       extends MapInterfaceTest<String, Map<Integer, Character>> {
387 
388     MapMapTests(boolean allowsNullValues, boolean supportsRemove,
389         boolean supportsClear, boolean supportsIteratorRemove) {
390       super(false, allowsNullValues, false, supportsRemove, supportsClear,
391           supportsIteratorRemove);
392     }
393 
394     @Override protected String getKeyNotInPopulatedMap() {
395       return "cat";
396     }
397 
398     @Override protected Map<Integer, Character> getValueNotInPopulatedMap() {
399       return ImmutableMap.of();
400     }
401 
402     /**
403      * The version of this test supplied by {@link MapInterfaceTest} fails for
404      * this particular map implementation, because {@code map.get()} returns a
405      * view collection that changes in the course of a call to {@code remove()}.
406      * Thus, the expectation doesn't hold that {@code map.remove(x)} returns the
407      * same value which {@code map.get(x)} did immediately beforehand.
408      */
409     @Override public void testRemove() {
410       final Map<String, Map<Integer, Character>> map;
411       final String keyToRemove;
412       try {
413         map = makePopulatedMap();
414       } catch (UnsupportedOperationException e) {
415         return;
416       }
417       keyToRemove = map.keySet().iterator().next();
418       if (supportsRemove) {
419         int initialSize = map.size();
420         map.get(keyToRemove);
421         map.remove(keyToRemove);
422         // This line doesn't hold - see the Javadoc comments above.
423         // assertEquals(expectedValue, oldValue);
424         assertFalse(map.containsKey(keyToRemove));
425         assertEquals(initialSize - 1, map.size());
426       } else {
427         try {
428           map.remove(keyToRemove);
429           fail("Expected UnsupportedOperationException.");
430         } catch (UnsupportedOperationException e) {
431           // Expected.
432         }
433       }
434       assertInvariants(map);
435     }
436   }
437 
438   private static abstract class RowMapTests extends MapMapTests {
439     RowMapTests(boolean allowsNullValues, boolean supportsRemove,
440         boolean supportsClear, boolean supportsIteratorRemove) {
441       super(allowsNullValues, supportsRemove, supportsClear,
442           supportsIteratorRemove);
443     }
444 
445     abstract Table<String, Integer, Character> makeTable();
446 
447     @Override protected Map<String, Map<Integer, Character>>
448         makePopulatedMap() {
449       Table<String, Integer, Character> table = makeTable();
450       populateTable(table);
451       return table.rowMap();
452     }
453 
454     void populateTable(Table<String, Integer, Character> table) {
455       table.put("foo", 1, 'a');
456       table.put("bar", 1, 'b');
457       table.put("foo", 3, 'c');
458     }
459 
460     @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
461       return makeTable().rowMap();
462     }
463   }
464 
465   public static class HashRowMapTests extends RowMapTests {
466     public HashRowMapTests() {
467       super(false, true, true, true);
468     }
469 
470     @Override Table<String, Integer, Character> makeTable() {
471       return HashBasedTable.create();
472     }
473   }
474 
475   public static class TreeRowMapTests extends RowMapTests {
476     public TreeRowMapTests() {
477       super(false, true, true, true);
478     }
479 
480     @Override Table<String, Integer, Character> makeTable() {
481       return TreeBasedTable.create();
482     }
483   }
484 
485   public static class TreeRowMapHeadMapTests extends RowMapTests {
486     public TreeRowMapHeadMapTests() {
487       super(false, true, true, true);
488     }
489 
490     @Override TreeBasedTable<String, Integer, Character> makeTable() {
491       TreeBasedTable<String, Integer, Character> table =
492           TreeBasedTable.create();
493       table.put("z", 1, 'a');
494       return table;
495     }
496 
497     @Override protected Map<String, Map<Integer, Character>>
498         makePopulatedMap() {
499       TreeBasedTable<String, Integer, Character> table = makeTable();
500       populateTable(table);
501       return table.rowMap().headMap("x");
502     }
503 
504     @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
505       return makeTable().rowMap().headMap("x");
506     }
507 
508     @Override protected String getKeyNotInPopulatedMap() {
509       return "z";
510     }
511   }
512 
513   public static class TreeRowMapTailMapTests extends RowMapTests {
514     public TreeRowMapTailMapTests() {
515       super(false, true, true, true);
516     }
517 
518     @Override TreeBasedTable<String, Integer, Character> makeTable() {
519       TreeBasedTable<String, Integer, Character> table =
520           TreeBasedTable.create();
521       table.put("a", 1, 'a');
522       return table;
523     }
524 
525     @Override protected Map<String, Map<Integer, Character>>
526         makePopulatedMap() {
527       TreeBasedTable<String, Integer, Character> table = makeTable();
528       populateTable(table);
529       return table.rowMap().tailMap("b");
530     }
531 
532     @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
533       return makeTable().rowMap().tailMap("b");
534     }
535 
536     @Override protected String getKeyNotInPopulatedMap() {
537       return "a";
538     }
539   }
540 
541   public static class TreeRowMapSubMapTests extends RowMapTests {
542     public TreeRowMapSubMapTests() {
543       super(false, true, true, true);
544     }
545 
546     @Override TreeBasedTable<String, Integer, Character> makeTable() {
547       TreeBasedTable<String, Integer, Character> table =
548           TreeBasedTable.create();
549       table.put("a", 1, 'a');
550       table.put("z", 1, 'a');
551       return table;
552     }
553 
554     @Override protected Map<String, Map<Integer, Character>>
555         makePopulatedMap() {
556       TreeBasedTable<String, Integer, Character> table = makeTable();
557       populateTable(table);
558       return table.rowMap().subMap("b", "x");
559     }
560 
561     @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
562       return makeTable().rowMap().subMap("b", "x");
563     }
564 
565     @Override protected String getKeyNotInPopulatedMap() {
566       return "z";
567     }
568   }
569 
570   private static final Function<String, Character> FIRST_CHARACTER =
571       new Function<String, Character>() {
572         @Override
573         public Character apply(String input) {
574           return input == null ? null : input.charAt(0);
575         }
576       };
577 
578   public static class TransformValueRowMapTests extends RowMapTests {
579     public TransformValueRowMapTests() {
580       super(false, true, true, true);
581     }
582 
583     @Override Table<String, Integer, Character> makeTable() {
584       Table<String, Integer, String> original = HashBasedTable.create();
585       return Tables.transformValues(original, FIRST_CHARACTER);
586     }
587 
588     @Override
589     protected Map<String, Map<Integer, Character>> makePopulatedMap() {
590       Table<String, Integer, String> table = HashBasedTable.create();
591       table.put("foo", 1, "apple");
592       table.put("bar", 1, "banana");
593       table.put("foo", 3, "cat");
594       return Tables.transformValues(table, FIRST_CHARACTER).rowMap();
595     }
596   }
597 
598   public static class UnmodifiableHashRowMapTests extends RowMapTests {
599     public UnmodifiableHashRowMapTests() {
600       super(false, false, false, false);
601     }
602 
603     @Override Table<String, Integer, Character> makeTable() {
604       Table<String, Integer, Character> original = HashBasedTable.create();
605       return Tables.unmodifiableTable(original);
606     }
607 
608     @Override
609     protected Map<String, Map<Integer, Character>> makePopulatedMap() {
610       Table<String, Integer, Character> table = HashBasedTable.create();
611       table.put("foo", 1, 'a');
612       table.put("bar", 1, 'b');
613       table.put("foo", 3, 'c');
614       return Tables.unmodifiableTable(table).rowMap();
615     }
616   }
617 
618   public static class UnmodifiableTreeRowMapTests extends RowMapTests {
619     public UnmodifiableTreeRowMapTests() {
620       super(false, false, false, false);
621     }
622 
623     @Override RowSortedTable<String, Integer, Character> makeTable() {
624       RowSortedTable<String, Integer, Character> original = TreeBasedTable.create();
625       return Tables.unmodifiableRowSortedTable(original);
626     }
627 
628     @Override
629     protected SortedMap<String, Map<Integer, Character>> makePopulatedMap() {
630       RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
631       table.put("foo", 1, 'a');
632       table.put("bar", 1, 'b');
633       table.put("foo", 3, 'c');
634       return Tables.unmodifiableRowSortedTable(table).rowMap();
635     }
636   }
637 
638   private static abstract class ColumnMapTests extends MapMapTests {
639     ColumnMapTests(boolean allowsNullValues, boolean supportsRemove,
640         boolean supportsClear, boolean supportsIteratorRemove) {
641       super(allowsNullValues, supportsRemove, supportsClear,
642           supportsIteratorRemove);
643     }
644 
645     abstract Table<Integer, String, Character> makeTable();
646 
647     @Override protected Map<String, Map<Integer, Character>>
648         makePopulatedMap() {
649       Table<Integer, String, Character> table = makeTable();
650       table.put(1, "foo", 'a');
651       table.put(1, "bar", 'b');
652       table.put(3, "foo", 'c');
653       return table.columnMap();
654     }
655 
656     @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
657       return makeTable().columnMap();
658     }
659   }
660 
661   public static class HashColumnMapTests extends ColumnMapTests {
662     public HashColumnMapTests() {
663       super(false, true, true, false);
664     }
665 
666     @Override Table<Integer, String, Character> makeTable() {
667       return HashBasedTable.create();
668     }
669   }
670 
671   public static class TreeColumnMapTests extends ColumnMapTests {
672     public TreeColumnMapTests() {
673       super(false, true, true, false);
674     }
675 
676     @Override Table<Integer, String, Character> makeTable() {
677       return TreeBasedTable.create();
678     }
679   }
680 
681   public static class TransformValueColumnMapTests extends ColumnMapTests {
682     public TransformValueColumnMapTests() {
683       super(false, true, true, false);
684     }
685 
686     @Override Table<Integer, String, Character> makeTable() {
687       Table<Integer, String, String> original = HashBasedTable.create();
688       return Tables.transformValues(original, FIRST_CHARACTER);
689     }
690 
691     @Override
692     protected Map<String, Map<Integer, Character>> makePopulatedMap() {
693       Table<Integer, String, String> table = HashBasedTable.create();
694       table.put(1, "foo", "apple");
695       table.put(1, "bar", "banana");
696       table.put(3, "foo", "cat");
697       return Tables.transformValues(table, FIRST_CHARACTER).columnMap();
698     }
699   }
700 
701   public static class UnmodifiableHashColumnMapTests extends ColumnMapTests {
702     public UnmodifiableHashColumnMapTests() {
703       super(false, false, false, false);
704     }
705 
706     @Override Table<Integer, String, Character> makeTable() {
707       Table<Integer, String, Character> original = HashBasedTable.create();
708       return Tables.unmodifiableTable(original);
709     }
710 
711     @Override
712     protected Map<String, Map<Integer, Character>> makePopulatedMap() {
713       Table<Integer, String, Character> table = HashBasedTable.create();
714       table.put(1, "foo", 'a');
715       table.put(1, "bar", 'b');
716       table.put(3, "foo", 'c');
717       return Tables.unmodifiableTable(table).columnMap();
718     }
719   }
720 
721   public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests {
722     public UnmodifiableTreeColumnMapTests() {
723       super(false, false, false, false);
724     }
725 
726     @Override Table<Integer, String, Character> makeTable() {
727       RowSortedTable<Integer, String, Character> original = TreeBasedTable.create();
728       return Tables.unmodifiableRowSortedTable(original);
729     }
730 
731     @Override
732     protected Map<String, Map<Integer, Character>> makePopulatedMap() {
733       RowSortedTable<Integer, String, Character> table = TreeBasedTable.create();
734       table.put(1, "foo", 'a');
735       table.put(1, "bar", 'b');
736       table.put(3, "foo", 'c');
737       return Tables.unmodifiableRowSortedTable(table).columnMap();
738     }
739   }
740 }
741